package com.javastar.modules.security.config;

import com.javastar.modules.security.exception.SysAuthenticationEntryPoint;
import com.javastar.modules.security.filter.TokenAuthenticationFilter;
import com.javastar.modules.security.handler.SysAccessDeniedHandler;
import com.javastar.modules.security.handler.SysAuthenticationFailureHandler;
import com.javastar.modules.security.handler.SysAuthenticationSuccessHandler;
import com.javastar.modules.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * 系统安全配置类
 *
 * @author zxx
 * @date 2023/7/26
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SysSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private SysAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private SysAuthenticationFailureHandler authenticationFailureHandler;
    @Autowired
    private SysAccessDeniedHandler accessDeniedHandler;
    @Autowired
    private TokenAuthenticationFilter tokenAuthenticationFilter;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private SysAuthenticationEntryPoint authenticationEntryPoint;

    public static List<String> whitePath = List.of(
            "/login",
            // knife4j接口文档请求
            "/doc.html",
            "/webjars/**",
            "/v2/**",
            "/swagger-resources/**",
            "/swagger-ui/**",
            "/assets/**"
    );

    /**
     * 密码编码器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        super.configure(auth);
    }

    /**
     * 认证管理器
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 核心配置方法
     * @param httpSecurity the {@link HttpSecurity} to modify
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .cors().and().csrf().disable()
                .authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                    // 放行白名单请求
                    .antMatchers(whitePath.toArray(new String[whitePath.size()])).permitAll()
                    .anyRequest().authenticated()
                .and()
                .formLogin()
                    // 自定义认证成功处理器
                    .successHandler(authenticationSuccessHandler)
                    // 自定义失败拦截器
                    .failureHandler(authenticationFailureHandler)
                    // 自定义登录拦截URI
                    .loginProcessingUrl("/login")
                    // 前端传入用户名密码参数名称
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                // 自定义认证失败类
                .exceptionHandling()
                    // 自定义权限不足处理类
                    .accessDeniedHandler(accessDeniedHandler)
                    // 无凭证异常处理器
                    .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                // 设置无状态的连接,即不创建session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 配置自己的jwt验证过滤器
        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        // disable page caching
        httpSecurity.headers().cacheControl();
    }
}
